package entries

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	apierrors "gitcode.com/astral-sphere/FreeDictionaryAPI-sdk-go/api-errors"
	"gitcode.com/astral-sphere/FreeDictionaryAPI-sdk-go/base"
	"gitcode.com/astral-sphere/FreeDictionaryAPI-sdk-go/components"
	"golang.org/x/text/language"
)

type WordRequest struct {
	Options base.RequestOptions
	// ISO 639-1/639-3 (2 or 3 letter) language code for word lookup, or
	// "all" to retrieve entries across all available languages.
	Language     language.Tag
	Word         string
	Translations bool
	Pretty       bool
}

// The path is `/entries/{language}/{word}`
func (r *WordRequest) BuildPath() (*url.URL, error) {
	// Create a base URL and then properly encode the path components
	baseURL, err := url.Parse(r.Options.BaseUrl)
	if err != nil {
		// If parsing the base URL fails, return as is
		return nil, &apierrors.UrlError{Reason: "Failed to parse", Url: baseURL}
	}

	path := baseURL.JoinPath("entries", r.Language.String(), r.Word)

	return path, nil
}

func (r *WordRequest) BuildUrl() (*url.URL, error) {
	path, err := r.BuildPath()
	if err != nil {
		// If parsing fails, fall back to the original approach
		if e, ok := err.(*apierrors.UrlError); ok {
			return nil, e
		} else {
			panic("Impossible type assertion failure")
		}
	}

	// Create query values
	queryValues := path.Query()

	if r.Translations {
		queryValues.Add("translations", strconv.FormatBool(r.Translations))
	}
	if r.Pretty {
		queryValues.Add("pretty", strconv.FormatBool(r.Pretty))
	}

	// Set the updated query parameters
	path.RawQuery = queryValues.Encode()

	return path, nil
}

func (r *WordRequest) Request() (*components.EntriesByLanguageAndWord, error) {
	// Build the URL for the request
	url, err := r.BuildUrl()
	if err != nil {
		return nil, err
	}

	// Create HTTP client with timeout
	client := &http.Client{
		Timeout: r.Options.TimeOut,
	}

	// Make the HTTP GET request
	resp, err := client.Get(url.String())
	if err != nil {
		return nil, fmt.Errorf("failed to make request: %w", err)
	}
	defer resp.Body.Close()

	// Check if the response status indicates success
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, &apierrors.StatusError{StatusCode: resp.StatusCode}
	}

	// Decode the JSON response
	var result components.EntriesByLanguageAndWord
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, &apierrors.UnMarshalError{Err: err}
	}

	return &result, nil
}
